package algo.math.evaluator.context.operator;

import java.lang.reflect.Type;

import algo.math.evaluator.base.IBase;
import algo.math.evaluator.base.exceptions.InvalidToken;
import algo.math.evaluator.base.exceptions.UnrecognizedToken;
import algo.math.evaluator.context.EvaluationContext;
import algo.math.evaluator.context.operator.interfaces.IOperator;
import algo.math.evaluator.value.IValue;
import algo.math.evaluator.value.IValue.Compare;
import algo.math.evaluator.value.Value;

// TODO: Auto-generated Javadoc
/**
 * Represent an arithmetic addition.
 */
public class Addition extends OperatorPrimitive {

	private String _name = OperatorSymbol.PLUS.toString();
	
	private String _value= "+";
	
	private TokenType _type=TokenType.OPERATOR;
	
	private int _level=4;
	
	private int _nbOperands =2;

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getName()
	 */
	public String getName() {
		return _name;
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getLevel()
	 */
	public int getLevel() {
		return _level;
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#setLevel(int)
	 */
	@Override
	public void setLevel(int level) {
		return;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.tokenizer.interfaces.IToken#getType()
	 */
	public TokenType getType() {
		return _type;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.tokenizer.interfaces.IToken#setType(algo.math.evaluator.tokenizer.interfaces.IToken.TokenType)
	 */
	public void setType(TokenType type) {
		return;
	}
	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.tokenizer.interfaces.IToken#setValue(java.lang.String)
	 */
	public void setValue(String value) {
		return;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getValue()
	 */
	public String getValue() {
		return _value;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.IComponentObject#getComponentType()
	 */
	public Type getComponentType() {
			return IOperator.class;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.IComponentObject#getComponentId()
	 */
	public String getComponentId() {
			return _value;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.IComponentObject#getComponentName()
	 */
	public String getComponentName() {
			return _name;
	}
	
	
	private String[] getUnit (int value_start, int value_end, IBase b)throws UnrecognizedToken
	{
		
		 if (value_start ==value_end)
		 {
			if (value_start>(b.maxWeight()-1))
			 {
			 return new String [] {"0","false"};
			 }
			 return new String [] {b.toStringArray()[value_start],"true"};
		 }
			 
		 int  start_epoch = value_start;
		 int i=0;
		 Boolean firstepoch = true;
		 
		 while (start_epoch<value_end)
		 {

			 for ( i = value_start; i<= b.maxWeight();i++) 
			 {
				 start_epoch++;
				 if (start_epoch==value_end)
					 break; 
			 }
			 if (start_epoch==value_end)
				 break;
			 
			 value_start =0;
			 firstepoch = false;
		 }
		 
		 if (firstepoch)
			 return new String [] {b.toStringArray()[i-1],firstepoch.toString()};
		 
		 if (i<=0)
			 return new String [] {b.toStringArray()[0],firstepoch.toString()};
		 
		 return new String [] {b.toStringArray()[i],firstepoch.toString()};
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.OperatorPrimitive#resolve(algo.math.evaluator.value.IValue[])
	 */
	@Override
	protected IValue resolve(IValue[] operands) throws UnrecognizedToken, InvalidToken {
		
		IBase b = EvaluationContext.GetInstance().getBase();
		Boolean isNegative= false;
		
		if ((operands[0].isNegative()&& !operands[1].isNegative()))
		{
				operands[0].SetIsNegative(false);
				operands[1].SetIsNegative(false);
		
				if (operands[0].compareTo(operands[1])== Compare.SUPERIOR)
				{
					IValue v = new Substraction().evaluate(operands);
					v.SetIsNegative(true);
					return v;
				}
				else if (operands[0].compareTo(operands[1])== Compare.INFERIOR)
				{
					IValue v = new Substraction().evaluate(operands);
					v.SetIsNegative(false);
					return v;
				}
		}
		else if ((!operands[0].isNegative()&& operands[1].isNegative()))
		{
			if (operands[0].compareTo(operands[1])== Compare.SUPERIOR)
			{
				operands[1].SetIsNegative(false);
				IValue v = new Substraction().evaluate(operands);
				v.SetIsNegative(false);
				return v;
			}
			else if (operands[0].compareTo(operands[1])== Compare.INFERIOR)
			{
				operands[1].SetIsNegative(false);
				IValue v = new Substraction().evaluate(operands);
				v.SetIsNegative(true);
				return v;
			}
		
		}
		else if ((operands[0].isNegative()&& operands[1].isNegative()))
		{
				isNegative = true;
				operands[0].SetIsNegative(false);
				operands[1].SetIsNegative(false);
		}
		
		
		String [] values = prepare(operands[0],operands[1],b);
		
		int carry1=0;
		String intValue ="", decValue ="";
		IValue intV,intD;
		
		String int1= values [0];
		String int2= values [1];
		String dec1= values [2];
		String dec2= values [3];
		
		for (int i =dec1.length()-1; i>=0;i--)
		{
			String [] out =  getUnit(
					b.weightOf(dec1.charAt(i))+carry1,
					b.weightOf(dec1.charAt(i))+carry1 +b.weightOf(dec2.charAt(i)),b);
				if (out[1].equals("false")){
					carry1=1;
				}
				
				else
				{
					carry1=0;
				}
				decValue = out[0]+ decValue;
		}
		
		intD = new Value ();
		intD.setValue(decValue, b);
		
		for (int i =int1.length()-1; i>=0;i--)
		{
			String [] out =  getUnit(
					b.weightOf(int1.charAt(i))+carry1,
					b.weightOf(int1.charAt(i))+carry1 +b.weightOf(int2.charAt(i)),b);
				if (out[1].equals("false")){
					carry1=1;
				}
				
				else
				{
					carry1=0;
				}
				intValue = out[0]+ intValue;
		}
		
		if (carry1==1)
			intValue = b.valueOf(1)+ intValue;
		
		 intV = new Value ();
		intV.setValue(intValue+ '.'+decValue, b);
		
		if (isNegative)
			intV.SetIsNegative(true);
		
		return intV;
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getNbOperands()
	 */
	@Override
	public int getNbOperands() {
		return _nbOperands;
	}


	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#evaluate(algo.math.evaluator.value.IValue[])
	 */
	@Override
	public IValue evaluate(IValue[] operands) throws UnrecognizedToken, InvalidToken {
		return resolve(operands);
	}
	
}
